﻿// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// licensed under the GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// 
// You may use this code under the terms of the GNU General Public 
// License (GPL) Version 2.
using System;
using System.Collections.Generic;
using System.Net;

namespace Salient.Net
{
    /// <summary>
    /// A simple gateway assembler. This class, once instantiated
    /// can be, with care, treated as a singleton. It maintains a single
    /// default gateway that is always available. 
    /// 
    /// Alternately, you may request a gateway instance for a particular
    /// host via the indexer. If a gateway does not exist for the host
    /// specified, one is created and added to the internal list.
    /// Subsequent calls to this factory with the same url will return
    /// the same gateway instance.
    /// 
    /// If GatewayPerHost is specified, all requests to the indexer will
    /// return the default gateway. This allows calling code to be agile
    /// in responding to changing conditions without the need to re code.
    /// </summary>
    public class GatewayFactory
    {



        private TimeSpan _defaultCacheDuration = new TimeSpan(0, 1, 0);
        private Type _gatewayType = typeof(Gateway);
        private Type _cacheType = typeof(GatewayCache);
        private int _throttleInterval = 170;
        private bool _gatewayPerHost = true;
        private string _userAgent = "SOAPI.CS";
        private IGateway _defaultGateway;
        private Dictionary<string, IGateway> _gateways = new Dictionary<string, IGateway>();
        private readonly object _syncLock = new object();

        ///<summary>
        /// Create a new gateway factory with default values.
        ///</summary>
        public GatewayFactory()
        {
            _defaultGateway = Create();
        }

        ///<summary>
        ///</summary>
        ///<param name="cacheType">The typef of IGatewayCache to inject into new gateway instances. Default is <see cref="GatewayCache"/></param>
        ///<param name="defaultCacheDuration">The default period of time to cache requests. Default is 1 minute.</param>
        ///<param name="gatewayType">The type of IGateway to instantiate. Default is <see cref="Gateway"/>  </param>
        ///<param name="throttleInterval">The minimum length of time to allow between requests. The default is 170ms (6.3 request per second)</param>
        ///<param name="gatewayPerHost">If false, accessing the indexer returns the default gateway. Default false.</param>
        ///<param name="userAgent">The user-agent header to apply to all requests. Default SOAPI.CS</param>
        public GatewayFactory(Type cacheType, TimeSpan defaultCacheDuration, Type gatewayType, int throttleInterval,
                              bool gatewayPerHost, string userAgent)
        {
            _gatewayType = gatewayType;
            _cacheType = cacheType;
            _defaultCacheDuration = defaultCacheDuration;
            _throttleInterval = throttleInterval;
            _gatewayPerHost = gatewayPerHost;
            _userAgent = userAgent;
            _defaultGateway = Create();
        }

        ///<summary>
        /// Creates a gateway configured with the properties of this factory
        /// instance. 
        ///</summary>
        ///<returns></returns>
        public IGateway Create()
        {
            var requestCache = (IGatewayCache)Activator.CreateInstance(_cacheType);
            requestCache.DefaultCacheDuration = _defaultCacheDuration;
            var requestFactory = (IGateway)Activator.CreateInstance(_gatewayType);
            requestFactory.Cache = requestCache;
            requestFactory.ThrottleInterval = _throttleInterval;
            requestFactory.UserAgent = _userAgent;
            _gateways = new Dictionary<string, IGateway>();
            return requestFactory;
        }

        ///<summary>
        /// A default gateway configured with the properties of this factory
        /// instance. If GatewayPerHost is true, this is the gateway instance
        /// that is returned from indexer calls.
        ///</summary>
        public IGateway DefaultGateway
        {
            get
            {
                lock (_syncLock)
                {
                    if (_defaultGateway == null)
                    {
                        _defaultGateway = Create();
                    }
                }
                return _defaultGateway;
            }
            set
            {
                lock (_syncLock)
                {
                    _defaultGateway = value;
                }

            }
        }


        ///<summary>
        /// Factory indexer. If a gateway has not been created for the 
        /// host portion of the endpoint url, one is instantiated and 
        /// placed in the collection.
        ///</summary>
        ///<param name="endpoint"></param>
        public IGateway this[string endpoint]
        {
            get
            {

                if (!_gatewayPerHost)
                {
                    return DefaultGateway;
                }

                endpoint = new Uri(endpoint.ToLower()).Host;
                lock (_syncLock)
                {
                    if (!_gateways.ContainsKey(endpoint))
                    {
                        _gateways.Add(endpoint, Create());
                    }
                }

                return _gateways[endpoint];
            }

        }


        /// <summary>
        /// The default period of time to cache requests. Default is 1 minute.
        /// Note - setting this propery recreates the default gateway.
        /// </summary>
        public TimeSpan DefaultCacheDuration
        {
            get
            {
                return _defaultCacheDuration;
            }
            set
            {
                lock (_syncLock)
                {

                    if (_defaultCacheDuration == value)
                        return;
                    _defaultCacheDuration = value;
                    _defaultGateway = null;
                }
            }
        }
        ///<summary>
        /// The type of IGateway to instantiate.
        /// Note - setting this propery recreates the default gateway.
        ///</summary>
        public Type GatewayType
        {
            get
            {
                return _gatewayType;
            }
            set
            {
                lock (_syncLock)
                {

                    if (_gatewayType == value)
                        return;
                    _gatewayType = value;
                    _defaultGateway = null;
                }
            }
        }
        /// <summary>
        /// The typef of IGatewayCache to inject into new gateway instances.
        /// Note - setting this propery recreates the default gateway.
        /// </summary>
        public Type CacheType
        {
            get
            {
                return _cacheType;
            }
            set
            {
                lock (_syncLock)
                {

                    if (_cacheType == value)
                        return;
                    _cacheType = value;
                }
            }
        }

        /// <summary>
        /// The minimum length of time to allow between requests.
        /// Note - setting this propery recreates the default gateway.
        /// </summary>
        public int ThrottleInterval
        {
            get
            {
                return _throttleInterval;
            }
            set
            {
                lock (_syncLock)
                {

                    if (_throttleInterval == value)
                        return;
                    _throttleInterval = value;
                    _defaultGateway = null;
                }
            }
        }
        ///<summary>
        /// If false, accessing the indexer returns the default gateway
        ///</summary>
        public bool GatewayPerHost
        {
            get
            {
                return _gatewayPerHost;
            }
            set
            {
                lock (_syncLock)
                {

                    if (_gatewayPerHost == value)
                        return;
                    _gatewayPerHost = value;
                }
            }
        }


        /// <summary>
        /// The user-agent header to apply to all requests.
        /// Note - setting this propery recreates the default gateway.
        /// </summary>
        public string UserAgent
        {
            get
            {
                return _userAgent;
            }
            set
            {
                lock (_syncLock)
                {
                    if (_userAgent == value)
                        return;
                    _userAgent = value;
                    _defaultGateway = null;
                }
                
            }
        }


    }
}

